//*****************************************************************************
//* Foxit Digital Rights Management Library
//*
//* Foxit Software Inc.
//* Copyright(C) 2005-2010, all rights reserved.
//*
//* The following code is copyrighted and contains proprietary information
//* and trade secrets of Foxit Software Inc.
//*****************************************************************************
/**
 * @addtogroup FDRM
 * @{
 */

/**
 * @file
 * @brief Define system-based interfaces, classes & functions.
 */

#ifndef _FDRM_SYSTEM
#define _FDRM_SYSTEM

//Classes defined in this header
class CFDRM_Object;
template <class T> class CFDRM_SmartPtr;
template<class baseType, class baseDataType, class baseClass> class CFDRM_KeyStringTemplate;
class CFDRM_BaseCryptoString;
	template<class baseType, class baseDataType, class baseClass> class CFDRM_CryptoStringTemplate;

//*****************************************************************************
//* Base object
//*****************************************************************************
/**
 * @name Base object for FDRM
 */
/*@{*/

/**
 * @brief Base object class for FDRM, all DRM classes should derive from this class.
 */
class CFDRM_Object : public CFX_Object
{
public:
	/** @brief	Default constructor. Initialize counter to 1. */
	CFDRM_Object() : m_nCount(1) {}
	/** @brief	Virtual destructor. Flexible way to destroy object by this class pointer. */
	virtual ~CFDRM_Object() {}

	/**
	 * @brief	Get a shared object pointer, internal counter increases.
	 * @return	Return the shared object pointer.
	 */
	virtual CFDRM_Object*	Retain() {m_nCount ++; return this;}

	/** @brief	Release the current instance. Decreases the internal counter, if it's zero, then delete the instance. */
	virtual void			Release() 
	{
		--m_nCount;
		if (m_nCount == 0) delete this;		
	}
	
protected:
	FX_DWORD	m_nCount;
};

/*@}*/

//*****************************************************************************
//* Smart pointer
//*****************************************************************************
template <class T>
class CFDRM_SmartPtr
{
public:
	CFDRM_SmartPtr(T *pObj) : m_pObj(pObj) {}
	~CFDRM_SmartPtr() {m_pObj->Release();}

			operator T*(void)	{return m_pObj;}
    T&		operator *(void)	{return *m_pObj;}
    T*		operator ->(void)	{return m_pObj;}

protected:
	T *m_pObj;
};

//*****************************************************************************
//* Key string
//*****************************************************************************
/**
 * @name Key string
 */
/*@{*/

/**
 * @Brief Key string template class, read-only, the memory will be erased to zero.
 */
template<class baseType, class baseDataType, class baseClass>
class CFDRM_KeyStringTemplate : public baseClass
{
public:
	/** Default constructor */
	CFDRM_KeyStringTemplate() : baseClass() {}
	/** Construct from a base class object */
	CFDRM_KeyStringTemplate(const baseClass &obj) : baseClass(obj) {}
	/** Copy constructor */
	CFDRM_KeyStringTemplate(const CFDRM_KeyStringTemplate &kst) : baseClass((const baseType*)kst, kst.GetLength()) {}
	/** Destructor, erase buffer if necessary */
	~CFDRM_KeyStringTemplate() {if (baseClass::m_pData && ((baseDataType *)baseClass::m_pData)->m_nRefs < 2) EraseBuffer();}
 
	/**
	 * @brief	Create a copy object.
	 * @param[in] kst	original object to create a copy.
	 * @return	Return created object.
	 */
	static CFDRM_KeyStringTemplate* Create(const CFDRM_KeyStringTemplate &kst) {return FX_NEW CFDRM_KeyStringTemplate(kst);}

	/**
	 * @brief	Retrieve a buffer to store key data. This will erase prior buffer at first.
	 * @param[in] len	buffer length, in baseType
	 * @return	Return the allocated buffer pointer.
	 */
	baseType*	GetBuffer(FX_STRSIZE len)
	{
		if (baseClass::m_pData && ((baseDataType *)baseClass::m_pData)->m_nRefs < 2) EraseBuffer();
		baseType *pBuf = (baseType*)baseClass::GetBuffer(len);
		baseClass::ReleaseBuffer(len);
		return pBuf;
	}

	/** @brief	Subscript([]) operator overload, writable. */
	baseType& operator[](FX_STRSIZE index)
	{
		FXSYS_assert(baseClass::m_pData != NULL && ((baseDataType *)baseClass::m_pData)->m_String != NULL);
		FXSYS_assert(index >= 0 && index < ((baseDataType *)baseClass::m_pData)->m_nDataLength);
		return (baseType&)((baseDataType *)baseClass::m_pData)->m_String[index];
	}

	/** @brief	Subscript([]) operator overload, read-only. */
	baseType operator[](FX_STRSIZE index) const
	{
		FXSYS_assert(baseClass::m_pData != NULL && ((baseDataType *)baseClass::m_pData)->m_String != NULL);
		FXSYS_assert(index >= 0 && index < ((baseDataType *)baseClass::m_pData)->m_nDataLength);
		return (baseType)((baseDataType *)baseClass::m_pData)->m_String[index];
	}

	/** @brief	override assignment operator */
	CFDRM_KeyStringTemplate& operator = (const CFDRM_KeyStringTemplate &kst)
	{
		if (baseClass::m_pData && ((baseDataType *)baseClass::m_pData)->m_nRefs < 2) EraseBuffer();
		FX_STRSIZE len = kst.GetLength();
		baseType *pBuf = (baseType*)baseClass::GetBuffer(len);
		FXSYS_memcpy32(pBuf, (const baseType*)kst, len * sizeof(baseType));
		baseClass::ReleaseBuffer(len);
		return *this;
	}

	/** @brief	comparison(==) operator overload. case-sensitive. */
	bool operator == (const CFDRM_KeyStringTemplate& kst) const
	{
		FX_STRSIZE len = baseClass::GetLength();
		if (kst.GetLength() != len) return FALSE;
		return FXSYS_memcmp32((const baseType*)kst, baseClass::operator const baseType*(), len * sizeof(baseType)) == 0;
	}

	/** @brief	comparison(!=) operator overload. case-sensitive. */
	bool operator != (const CFDRM_KeyStringTemplate& kst) const
	{
		FX_STRSIZE len = baseClass::GetLength();
		if (kst.GetLength() != len) return TRUE;
		return FXSYS_memcmp32((const baseType*)kst, baseClass::operator const baseType*(), len * sizeof(baseType)) != 0;
	}

	/**
	 * @brief	Get number of data, in baseType.
	 * @return	The Length of the key string.
	 */
	FX_STRSIZE	GetLength() const {return baseClass::GetLength();}

	/** @brief	Empty the key string. */
	void Empty()
	{
		if (baseClass::m_pData) EraseBuffer();
		baseClass::Empty();
	}

protected:
	void EraseBuffer()
	{
		if (!((baseDataType *)baseClass::m_pData)->m_String) return;
		FXSYS_memset32(((baseDataType *)baseClass::m_pData)->m_String, 0, ((baseDataType *)baseClass::m_pData)->m_nDataLength);
	}
};

/** Defines a byte key string type. */
typedef CFDRM_KeyStringTemplate<FX_BYTE, CFX_StringData, CFX_ByteString>	CFDRM_ByteKeyString;
/** Defines a wide key string type. */
typedef CFDRM_KeyStringTemplate<FX_WCHAR, CFX_StringDataW, CFX_WideString>	CFDRM_WideKeyString;

/*@}*/

//*****************************************************************************
//* Crypto string
//*****************************************************************************
/**
 * @name Crypto string
 */
/*@{*/

/**
 * @Brief Base crypto string, used to store encrypted data.
 */
class CFDRM_BaseCryptoString : public CFX_Object
{
protected:
	/** Default constructor */
	CFDRM_BaseCryptoString();
	/** Copy constructor */
	CFDRM_BaseCryptoString(const CFDRM_BaseCryptoString &bcs);
	/** Destructor */
	~CFDRM_BaseCryptoString();

	/**
	 * @brief	Set key string. Key string will be encrypted internally.
	 * @param[in] pStr		points to the key string buffer, it should be valid(not a null pointer).
	 * @param[in] len		the length of key string, in bytes.
	 */
	void		SetKeyString(FX_LPCBYTE pStr, FX_STRSIZE len);
	/**
	 * @brief	Get key string. Key string will be decrypted internally.
	 * @param[out] pStr		points to the key string buffer. If it's a null pointer, this method returns the length of key string only.
	 * @return	return the length of decrypted key string, in bytes.
	 */
	FX_STRSIZE	GetKeyString(FX_LPBYTE pStr = NULL) const;
	/** @brief	Empty internal key string data. */
	void		Empty();

	/** @brief	Internal data */
	FX_LPVOID	m_pData;
};

/**
 * @Brief Template crypto string, derived from CFDRM_BaseCryptoString.
 */
template<class baseType, class baseDataType, class baseClass>
class CFDRM_CryptoStringTemplate : public CFDRM_BaseCryptoString
{
public:
	/** @brief	Type definition for the corresponding key string */
	typedef CFDRM_KeyStringTemplate<baseType, baseDataType, baseClass>	FDRM_KST;

	/** Default constructor */
	CFDRM_CryptoStringTemplate() : CFDRM_BaseCryptoString() {}
	/** Copy constructor */
	CFDRM_CryptoStringTemplate(const CFDRM_CryptoStringTemplate &cst) : CFDRM_BaseCryptoString(cst) {}
	/** Destructor */
	~CFDRM_CryptoStringTemplate() {}

	/**
	 * @brief	Set key string. Key string will be encrypted internally.
	 * @param[in] keyString		the primitive key string to be stored.
	 */
	void SetKeyString(const FDRM_KST &keyString)
	{
		CFDRM_BaseCryptoString::SetKeyString((FX_LPCBYTE)(const baseType*)keyString, keyString.GetLength() * sizeof(baseType));
	}

	/**
	 * @brief	Get key string. Key string will be decrypted internally.
	 * @param[out] keyString	stores the decrypted key string.
	 * @return	return TRUE if successful, or FALSE if internal key string is empty.
	 */
	FX_BOOL GetKeyString(FDRM_KST &keyString) const
	{
		FX_STRSIZE len = CFDRM_BaseCryptoString::GetKeyString(NULL);
		if (len < 1) return FALSE;
		FX_LPBYTE pBuf = (FX_LPBYTE)keyString.GetBuffer(len / sizeof(baseType));
		CFDRM_BaseCryptoString::GetKeyString(pBuf);
		return TRUE;
	}

	/**
	 * @brief	Determine whether the internal key string is empty or not.
	 * @return	return TRUE if internal key string is empty, or FALSE.
	 */
	FX_BOOL IsEmpty() const {return CFDRM_BaseCryptoString::GetKeyString(NULL) < 1;}

	/** @brief	Empty internal key string data. */
	void Empty() {CFDRM_BaseCryptoString::Empty();}
};

/** Defines a byte crypto string type. */
typedef CFDRM_CryptoStringTemplate<FX_BYTE, CFX_StringData, CFX_ByteString>		CFDRM_ByteCryptoString;
/** Defines a wide crypto string type. */
typedef CFDRM_CryptoStringTemplate<FX_WCHAR, CFX_StringDataW, CFX_WideString>	CFDRM_WideCryptoString;

/*@}*/

#endif //_FDRM_SYSTEM

/** @} */
